/*
  ==============================================================================

   This file is part of the JUCE library - "Jules' Utility Class Extensions"
   Copyright 2004-10 by Raw Material Software Ltd.

  ------------------------------------------------------------------------------

   JUCE can be redistributed and/or modified under the terms of the GNU General
   Public License (Version 2), as published by the Free Software Foundation.
   A copy of the license is included in the JUCE distribution, or can be found
   online at www.gnu.org/licenses.

   JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

  ------------------------------------------------------------------------------

   To release a closed-source product which uses JUCE, commercial licenses are
   available: visit www.rawmaterialsoftware.com/juce for more information.

  ==============================================================================
*/

#ifndef __JUCE_AFFINETRANSFORM_JUCEHEADER__
#define __JUCE_AFFINETRANSFORM_JUCEHEADER__


//==============================================================================
/**
    Represents a 2D affine-transformation matrix.

    An affine transformation is a transformation such as a rotation, scale, shear,
    resize or translation.

    These are used for various 2D transformation tasks, e.g. with Path objects.

    @see Path, Point, Line
*/
class JUCE_API  AffineTransform
{
public:
    //==============================================================================
    /** Creates an identity transform. */
    AffineTransform() throw();

    /** Creates a copy of another transform. */
    AffineTransform (const AffineTransform& other) throw();

    /** Creates a transform from a set of raw matrix values.

        The resulting matrix is:

            (mat00 mat01 mat02)
            (mat10 mat11 mat12)
            (  0     0     1  )
    */
    AffineTransform (float mat00, float mat01, float mat02,
                     float mat10, float mat11, float mat12) throw();

    /** Copies from another AffineTransform object */
    AffineTransform& operator= (const AffineTransform& other) throw();

    /** Compares two transforms. */
    bool operator== (const AffineTransform& other) const throw();

    /** Compares two transforms. */
    bool operator!= (const AffineTransform& other) const throw();

    /** A ready-to-use identity transform, which you can use to append other
        transformations to.

        e.g. @code
        AffineTransform myTransform = AffineTransform::identity.rotated (.5f)
                                                               .scaled (2.0f);

        @endcode
    */
    static const AffineTransform identity;

    //==============================================================================
    /** Transforms a 2D co-ordinate using this matrix. */
    template <typename ValueType>
    void transformPoint (ValueType& x, ValueType& y) const throw()
    {
        const ValueType oldX = x;
        x = static_cast <ValueType> (mat00 * oldX + mat01 * y + mat02);
        y = static_cast <ValueType> (mat10 * oldX + mat11 * y + mat12);
    }

    /** Transforms two 2D co-ordinates using this matrix.
        This is just a shortcut for calling transformPoint() on each of these pairs of
        coordinates in turn. (And putting all the calculations into one function hopefully
        also gives the compiler a bit more scope for pipelining it).
    */
    template <typename ValueType>
    void transformPoints (ValueType& x1, ValueType& y1,
                          ValueType& x2, ValueType& y2) const throw()
    {
        const ValueType oldX1 = x1, oldX2 = x2;
        x1 = static_cast <ValueType> (mat00 * oldX1 + mat01 * y1 + mat02);
        y1 = static_cast <ValueType> (mat10 * oldX1 + mat11 * y1 + mat12);
        x2 = static_cast <ValueType> (mat00 * oldX2 + mat01 * y2 + mat02);
        y2 = static_cast <ValueType> (mat10 * oldX2 + mat11 * y2 + mat12);
    }

    /** Transforms three 2D co-ordinates using this matrix.
        This is just a shortcut for calling transformPoint() on each of these pairs of
        coordinates in turn. (And putting all the calculations into one function hopefully
        also gives the compiler a bit more scope for pipelining it).
    */
    template <typename ValueType>
    void transformPoints (ValueType& x1, ValueType& y1,
                          ValueType& x2, ValueType& y2,
                          ValueType& x3, ValueType& y3) const throw()
    {
        const ValueType oldX1 = x1, oldX2 = x2, oldX3 = x3;
        x1 = static_cast <ValueType> (mat00 * oldX1 + mat01 * y1 + mat02);
        y1 = static_cast <ValueType> (mat10 * oldX1 + mat11 * y1 + mat12);
        x2 = static_cast <ValueType> (mat00 * oldX2 + mat01 * y2 + mat02);
        y2 = static_cast <ValueType> (mat10 * oldX2 + mat11 * y2 + mat12);
        x3 = static_cast <ValueType> (mat00 * oldX3 + mat01 * y3 + mat02);
        y3 = static_cast <ValueType> (mat10 * oldX3 + mat11 * y3 + mat12);
    }

    //==============================================================================
    /** Returns a new transform which is the same as this one followed by a translation. */
    const AffineTransform translated (float deltaX,
                                      float deltaY) const throw();

    /** Returns a new transform which is a translation. */
    static const AffineTransform translation (float deltaX,
                                              float deltaY) throw();

    /** Returns a transform which is the same as this one followed by a rotation.

        The rotation is specified by a number of radians to rotate clockwise, centred around
        the origin (0, 0).
    */
    const AffineTransform rotated (float angleInRadians) const throw();

    /** Returns a transform which is the same as this one followed by a rotation about a given point.

        The rotation is specified by a number of radians to rotate clockwise, centred around
        the co-ordinates passed in.
    */
    const AffineTransform rotated (float angleInRadians,
                                   float pivotX,
                                   float pivotY) const throw();

    /** Returns a new transform which is a rotation about (0, 0). */
    static const AffineTransform rotation (float angleInRadians) throw();

    /** Returns a new transform which is a rotation about a given point. */
    static const AffineTransform rotation (float angleInRadians,
                                           float pivotX,
                                           float pivotY) throw();

    /** Returns a transform which is the same as this one followed by a re-scaling.
        The scaling is centred around the origin (0, 0).
    */
    const AffineTransform scaled (float factorX,
                                  float factorY) const throw();

    /** Returns a transform which is the same as this one followed by a re-scaling.
        The scaling is centred around the origin provided.
    */
    const AffineTransform scaled (float factorX, float factorY,
                                  float pivotX, float pivotY) const throw();

    /** Returns a new transform which is a re-scale about the origin. */
    static const AffineTransform scale (float factorX,
                                        float factorY) throw();

    /** Returns a new transform which is a re-scale centred around the point provided. */
    static const AffineTransform scale (float factorX, float factorY,
                                        float pivotX, float pivotY) throw();

    /** Returns a transform which is the same as this one followed by a shear.
        The shear is centred around the origin (0, 0).
    */
    const AffineTransform sheared (float shearX, float shearY) const throw();

    /** Returns a shear transform, centred around the origin (0, 0). */
    static const AffineTransform shear (float shearX, float shearY) throw();

    /** Returns a matrix which is the inverse operation of this one.

        Some matrices don't have an inverse - in this case, the method will just return
        an identity transform.
    */
    const AffineTransform inverted() const throw();

    /** Returns the transform that will map three known points onto three coordinates
        that are supplied.

        This returns the transform that will transform (0, 0) into (x00, y00),
        (1, 0) to (x10, y10), and (0, 1) to (x01, y01).
    */
    static const AffineTransform fromTargetPoints (float x00, float y00,
                                                   float x10, float y10,
                                                   float x01, float y01) throw();

    /** Returns the transform that will map three specified points onto three target points.
    */
    static const AffineTransform fromTargetPoints (float sourceX1, float sourceY1, float targetX1, float targetY1,
                                                   float sourceX2, float sourceY2, float targetX2, float targetY2,
                                                   float sourceX3, float sourceY3, float targetX3, float targetY3) throw();

    //==============================================================================
    /** Returns the result of concatenating another transformation after this one. */
    const AffineTransform followedBy (const AffineTransform& other) const throw();

    /** Returns true if this transform has no effect on points. */
    bool isIdentity() const throw();

    /** Returns true if this transform maps to a singularity - i.e. if it has no inverse. */
    bool isSingularity() const throw();

    /** Returns true if the transform only translates, and doesn't scale or rotate the
        points. */
    bool isOnlyTranslation() const throw();

    /** If this transform is only a translation, this returns the X offset.
        @see isOnlyTranslation
    */
    float getTranslationX() const throw()                   { return mat02; }

    /** If this transform is only a translation, this returns the X offset.
        @see isOnlyTranslation
    */
    float getTranslationY() const throw()                   { return mat12; }

    /** Returns the approximate scale factor by which lengths will be transformed.
        Obviously a length may be scaled by entirely different amounts depending on its
        direction, so this is only appropriate as a rough guide.
    */
    float getScaleFactor() const throw();

    //==============================================================================
    /* The transform matrix is:

        (mat00 mat01 mat02)
        (mat10 mat11 mat12)
        (  0     0     1  )
    */
    float mat00, mat01, mat02;
    float mat10, mat11, mat12;


private:
    //==============================================================================
    JUCE_LEAK_DETECTOR (AffineTransform);
};

#endif   // __JUCE_AFFINETRANSFORM_JUCEHEADER__
